package com.baidu.recognition.config.baidu.real.websocket;

import com.baidu.recognition.config.baidu.BaiduApi;
import com.baidu.recognition.config.baidu.BaiduConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author liuzhiqiang
 */
@Component("customWebSocketListener")
@Slf4j
public class CustomWebSocketListener extends WebSocketListener {


    /**
     * 16000的采样率，16bits=2bytes， 1000ms
     */
    public static final int BYTES_PER_MS = 16000 * 2 / 1000;
    /**
     * websocket一个数据帧 160ms
     */
    public static final int FRAME_MS = 160;
    /**
     * 一个数据帧的大小=5120bytes
     */
    public static final int BYTES_PER_FRAME = BYTES_PER_MS * FRAME_MS;
    private WebSocket webSocket;
    private InputStream inputStream;

    public static void sleep(long millis) {
        if (millis <= 0) {
            return;
        }
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public void connect() {
        // 创建一个连接
        OkHttpClient client = new OkHttpClient.Builder().connectTimeout(2000, TimeUnit.MILLISECONDS).build();
        String url = BaiduApi.REALTIME_ASR + UUID.randomUUID().toString();
        Request request = new Request.Builder().url(url).build();
        CustomWebSocketListener customWebSocketListener = this;
        client.newWebSocket(request, customWebSocketListener);
        client.dispatcher().executorService().shutdown();
    }

    public void setInputStream(MultipartFile file) {
        try {
            this.inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
        super.onClosed(webSocket, code, reason);
        this.webSocket = null;
        log.info("连接关闭");
    }

    @Override
    public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
        super.onClosing(webSocket, code, reason);
        webSocket.close(1000, "");
        log.info("连接关闭");
    }

    @Override
    public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {
        super.onFailure(webSocket, t, response);
        log.info("连接异常");
    }

    @Override
    public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
        super.onMessage(webSocket, text);
        log.info("收到消息【{}】", text);
    }

    @Override
    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
        super.onMessage(webSocket, bytes);
        log.info("收到消息【{}】", bytes);
    }

    @Override
    public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
        super.onOpen(webSocket, response);
        this.webSocket = webSocket;
    }

    public void sendFrame() {
        new Thread(() -> {
            // STEP 2.1 发送发送开始参数帧
            sendStartFrame(webSocket);
            // STEP 2.2 实时发送音频数据帧
            sendAudioFrames(webSocket);
            // STEP 2.4 发送结束帧
            sendFinishFrame(webSocket);
        }).start();
    }

    /**
     * STEP 2.1 发送发送开始参数帧
     *
     * @param webSocket WebSocket类
     * @throws JSONException Json解析异常
     */
    public void sendStartFrame(WebSocket webSocket) throws JSONException {


        JSONObject json = new JSONObject();
        json.put("type", "START");
        JSONObject params = new JSONObject();

        params.put("appid", BaiduConfig.appId);
        params.put("appkey", BaiduConfig.apiKey);

        params.put("dev_pid", 15372);
        params.put("cuid", "self_defined_server_id_like_mac_address");

        params.put("format", "pcm");
        params.put("sample", 16000);

        json.put("data", params);
        webSocket.send(json.toString());
    }

    /**
     * STEP 2.2 实时发送音频数据帧
     *
     * @param webSocket WebSocket类
     */
    public void sendAudioFrames(WebSocket webSocket) {
        log.info("begin to send DATA frames");
        // 一个帧 160ms的音频数据
        int bytesPerFrame = BYTES_PER_FRAME;
        byte[] buffer = new byte[bytesPerFrame];
        int readSize;
        long nextFrameSendTime = System.currentTimeMillis();
        do {
            // 数据帧之间需要有间隔时间， 间隔时间为上一帧的音频长度
            sleep(nextFrameSendTime - System.currentTimeMillis());
            try {
                readSize = this.inputStream.read(buffer);
            } catch (IOException | RuntimeException e) {
                log.warn("inputstream is closed:" + e.getClass().getSimpleName() + ":" + e.getMessage());
                readSize = -2;
            }
            // readSize = -1 代表流结束
            if (readSize > 0) {
                ByteString bytesToSend = ByteString.of(buffer, 0, readSize);
                nextFrameSendTime = System.currentTimeMillis() + readSize / BYTES_PER_MS;
                log.info("should wait to send next DATA Frame: " + readSize / BYTES_PER_MS
                        + "ms | send binary bytes size :" + readSize);
                webSocket.send(bytesToSend);
            }
        } while (readSize >= 0);
    }

    /**
     * STEP 2.4 发送结束帧
     *
     * @param webSocket WebSocket 类
     * @throws JSONException Json解析错误
     */
    public void sendFinishFrame(WebSocket webSocket) throws JSONException {
        JSONObject json = new JSONObject();
        json.put("type", "FINISH");
        log.info("send FINISH FRAME:" + json.toString());
        webSocket.send(json.toString());
    }

    public WebSocket getWebSocket() {
        return webSocket;
    }
}
